Skip to content

Normalize plugin secret/connection refs + add usages SDK surface#521

Open
RhysSullivan wants to merge 6 commits intomainfrom
rs/normalize-google-discovery
Open

Normalize plugin secret/connection refs + add usages SDK surface#521
RhysSullivan wants to merge 6 commits intomainfrom
rs/normalize-google-discovery

Conversation

@RhysSullivan
Copy link
Copy Markdown
Owner

@RhysSullivan RhysSullivan commented May 4, 2026

Summary

Plugins (graphql, openapi, mcp, google-discovery) now store every direct secret/connection reference in proper relational columns/child tables instead of JSON blobs. The SDK gains usagesForSecret / usagesForConnection so the Secrets and Connections tabs can show what's referencing each one — and so deletes can be RESTRICTed when in use (HTTP 409 SecretInUseError / ConnectionInUseError) instead of leaving dangling references.

What's in the diff

  • Per-plugin schema normalization:
    • graphql_source → flat auth_kind / auth_connection_id columns; new graphql_source_header, graphql_source_query_param child tables
    • openapi_source_binding → flat kind / secret_id / connection_id / text_value; new openapi_source_query_param, openapi_source_spec_fetch_header, openapi_source_spec_fetch_query_param
    • mcp_source → 7 flat auth columns; new mcp_source_header, mcp_source_query_param
    • google_discovery_source → flat auth + scopes columns; new google_discovery_source_credential_header, google_discovery_source_credential_query_param
  • Migrations for both apps/local (sqlite, 0007–0010) and apps/cloud (pg, 0008–0010). Each backfills via json_each / jsonb_each then drops the old JSON columns.
  • Migration tests (4 e2e tests on hand-seeded pre-shape DBs, incl. a colon-collision regression).
  • SDK surface: usagesForSecret(scopeId, secretId) / usagesForConnection(scopeId, connectionId) on PluginSpec, fanned out by the executor; SecretInUseError / ConnectionInUseError on the secrets/connections delete paths; GET /scopes/:scopeId/{secrets,connections}/:id/usages HTTP endpoints.
  • UI: Secrets and Connections tabs show a usage footer per row.
  • Cloud regen of apps/cloud/src/services/executor-schema.ts.
  • Bug fix: child-row primary keys use JSON.stringify([sourceId, name]) instead of ${sourceId}:${name} so user-provided separators can't collide.
  • Lockfile: @rhyssul/portless 0.13.0 → 0.13.3.

Test plan

  • Backup local sqlite, start dev server, confirm migrations apply cleanly
  • Existing graphql/openapi/mcp/google-discovery sources still invoke (auth resolves from new columns)
  • Secrets tab — used secret shows source(s) in footer; unused empty
  • Connections tab — same
  • Delete a used secret → 409 with in-use message
  • Delete the source first, then secret → succeeds
  • Edit a source's headers/auth, save, reload, verify persisted

Copy link
Copy Markdown
Owner Author

RhysSullivan commented May 4, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 4, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
🔵 In progress
View logs
executor-cloud 6437df1 May 05 2026, 06:49 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 4, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
🔵 In progress
View logs
executor-marketing 6437df1 May 05 2026, 06:49 PM

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 4, 2026

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@521

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@521

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@521

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@521

@executor-js/storage-core

npm i https://pkg.pr.new/@executor-js/storage-core@521

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@521

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@521

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@521

@executor-js/plugin-google-discovery

npm i https://pkg.pr.new/@executor-js/plugin-google-discovery@521

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@521

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@521

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@521

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@521

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@521

executor

npm i https://pkg.pr.new/executor@521

commit: 2698722

@RhysSullivan RhysSullivan force-pushed the rs/normalize-google-discovery branch from f2d49d3 to 41b0ace Compare May 4, 2026 19:50
@RhysSullivan RhysSullivan changed the base branch from rs/normalize-mcp to graphite-base/521 May 5, 2026 18:10
Plugins gain optional `usagesForSecret` / `usagesForConnection` callbacks
that the executor fans out across via `executor.secrets.usages(id)` and
`executor.connections.usages(id)`. Secret/connection removal is now
RESTRICT — refuses with `SecretInUseError` / `ConnectionInUseError` when
any plugin reports the id as in use, surfacing the count to the caller.

API gains GET `/secrets/:id/usages` and `/connections/:id/usages`
endpoints. React atoms and a small "Used by …" footer on each row in
the Secrets and Connections tabs round out the surface.

No plugin implements `usagesForSecret`/`usagesForConnection` yet, so
the footer renders nothing and remove still succeeds for everything.
Plugin migration lands in follow-up commits.
Move secret/connection references out of the JSON columns on
graphql_source (auth, headers, query_params) into:
- two new flat columns auth_kind + auth_connection_id (indexed)
- two child tables graphql_source_header / graphql_source_query_param,
  each with kind/text_value/secret_id/secret_prefix and an indexed
  secret_id

This lets usagesForSecret and usagesForConnection answer "where is X
referenced?" with a single indexed SELECT instead of scanning JSON, and
secrets.remove / connections.remove now correctly RESTRICT when a
graphql source still references the id.

Migration 0007 backfills existing JSON via json_each + json_extract,
then drops the old columns. Three tests cover the backfill against a
hand-seeded pre-migration DB; three plugin-level tests cover usage
fan-out and remove RESTRICT.
Move secret/connection references out of the JSON columns on
openapi_source and openapi_source_binding into:
- openapi_source_binding gains kind/secret_id/connection_id/text_value
  columns (replacing the discriminated-union json value)
- openapi_source_query_param: child table for query_params, mirrors
  graphql_source_query_param shape
- openapi_source_spec_fetch_header / spec_fetch_query_param: child
  tables for invocation_config.specFetchCredentials.{headers,queryParams}

Drops openapi_source.query_params and openapi_source.invocation_config
JSON columns. headers and oauth2 stay JSON because they hold slot
names rather than direct refs — the slots resolve through bindings,
which ARE normalized.

Plugin gains usagesForSecret / usagesForConnection that fan out across
all four locations in one indexed SELECT each. secrets.remove now
correctly RESTRICTs when an openapi binding still uses the secret.

Migration 0008 backfills via json_extract / json_each, then drops the
old columns. Three migration tests cover the backfill from a
hand-seeded pre-migration DB; two plugin-level tests cover usage
fan-out and remove RESTRICT.
Move secret/connection references out of the JSON `mcp_source.config`
column into:
- Flat columns on mcp_source: auth_kind, auth_header_name,
  auth_secret_id, auth_secret_prefix, auth_connection_id,
  auth_client_id_secret_id, auth_client_secret_secret_id (each indexed
  where it carries a ref). Replaces the McpConnectionAuth json blob.
- mcp_source_header / mcp_source_query_param: child tables for the
  remote source's SecretBackedMap entries (same shape as graphql /
  openapi child tables).

The remaining structural fields (transport, endpoint, command, args,
etc.) stay as JSON in `config` because they're plugin-private and vary
by transport. mcp_binding.binding stays JSON too — McpToolBinding
carries no refs and inputSchema/outputSchema are arbitrary user JSON.

Plugin gains usagesForSecret / usagesForConnection. Migration 0009
backfills via json_extract / json_each, then strips the extracted
fields with json_remove. Three migration tests cover backfill from a
hand-seeded pre-migration DB; two plugin-level tests cover usage
fan-out across header-auth and oauth2 sources.
Move secret/connection references out of the JSON
google_discovery_source.config column into:
- Flat columns on google_discovery_source: auth_kind,
  auth_connection_id, auth_client_id_secret_id,
  auth_client_secret_secret_id, auth_scopes (each indexed where it
  carries a ref). Replaces the GoogleDiscoveryAuth json.
- google_discovery_source_credential_header /
  google_discovery_source_credential_query_param: child tables for the
  optional GoogleDiscoveryFetchCredentials SecretBackedMap entries.

The remaining structural fields (discoveryUrl, service, version,
rootUrl, servicePath) stay in `config` as JSON — they're plugin-private
and never carry refs. google_discovery_binding stays JSON too: the
GoogleDiscoveryMethodBinding has no refs, and method input/output
schemas are arbitrary user data.

Plugin gains usagesForSecret / usagesForConnection. Migration 0010
backfills via json_extract / json_each, then strips the extracted
fields with json_remove. Three migration tests cover backfill from a
hand-seeded pre-migration DB; one plugin-level test covers usage
fan-out across auth_*_secret_id and auth_connection_id.
@RhysSullivan RhysSullivan force-pushed the rs/normalize-google-discovery branch from 41b0ace to 2698722 Compare May 5, 2026 18:27
@RhysSullivan RhysSullivan changed the base branch from graphite-base/521 to rs/normalize-mcp May 5, 2026 18:27
@RhysSullivan RhysSullivan marked this pull request as ready for review May 5, 2026 18:32
@RhysSullivan RhysSullivan changed the base branch from rs/normalize-mcp to main May 5, 2026 18:42
@RhysSullivan RhysSullivan changed the title Normalize google-discovery plugin secret/connection refs Normalize plugin secret/connection refs + add usages SDK surface May 5, 2026
Merge graphql/openapi/mcp/google-discovery normalization into one
migration per app: 0007_normalize_plugin_secret_refs (local sqlite)
and 0008_normalize_plugin_secret_refs (cloud pg, mcp-only since cloud
doesn't ship google-discovery).

Each test now imports a shared PRE_0007_SQL helper that seeds all four
plugins' parent tables at the pre-migration shape — needed because the
single consolidated migration touches every plugin in one transaction.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant